人工神经网络(artificial neural network)可谓玄乎,这边也不做一个详细解释。那大概是个什么工作原理呢?经常可以看见神经网络的网络图,每一层都有很多圈圈,那这些圈圈是什么呢?
人工神经网络每一层都指的是特征,层数不同,特征的深度也就不同。比如第一层2个圈,即2个输入那就是样本有两个特征作为输入,到了第二层有3个圈了,那就是前面一层的2个特征根据不同的权重组合成了这3个特征。最后输出层的个数即为我们需要分类数,输出的值为属于每一个类别的概率。
opencv中人工神经网络要控制的参数也就是层数和每层的特征数。
1、需要注意的是这边预测得到的该样本属于每个标签的概率,挑选概率最大。所以response的大小应该为1x标签数
2、上面的第一种转换的是独热编码的形式,即下面程序中的形式。但是如果不用独热编码的形式输出,那输出层只有一个特征,可以根据概率值的大小来判断是否属于该标签
// ex12.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
using namespace cv::ml;
int train_sample_nums = 200;
int main()
{
/******************数据集制作***************************/
Mat trainData(train_sample_nums, 2, CV_32FC1);
Mat trainClassess(train_sample_nums, 1, CV_32SC1); //注意标签的格式,有符号
//创建可视化图像
Mat img(500, 500, CV_8UC3, Scalar::all(0));
//样本点
Mat sample(1, 2, CV_32FC1);
Mat trainData1, trainData2, trainClasses1, trainClasses2;
RNG rng = RNG(-1);
//生成均值为(200,200),方差为(40,40)的随机数据
trainData1 = trainData.rowRange(0, train_sample_nums / 2);
rng.fill(trainData1, CV_RAND_NORMAL, Scalar(200, 200), Scalar(40, 40));
trainData2 = trainData.rowRange(train_sample_nums / 2, train_sample_nums);
rng.fill(trainData2, CV_RAND_NORMAL, Scalar(300, 300), Scalar(40, 40));
//trainClasses1和trainClassess的前100行绑定
trainClasses1 = trainClassess.rowRange(0, train_sample_nums / 2);
trainClasses1 = Scalar::all(0);
trainClasses2 = trainClassess.rowRange(train_sample_nums / 2, train_sample_nums);
trainClasses2 = Scalar::all(1);
cv::Ptr<cv::ml::ANN_MLP> ann = cv::ml::ANN_MLP::create();
int nlayers = 4; //隐藏层节点数量,即隐藏的特征数量
int numCharacters = 2; //输出层特征数量
Mat layers(1, 4, CV_32SC1);
layers.at<int>(0, 0) = trainData.cols; //输入层特征数量
layers.at<int>(0, 1) = nlayers;
layers.at<int>(0, 2) = 4;
layers.at<int>(0, 3) = 2; //这边输出层的特征数量是要和response_mat挂钩
ann->setLayerSizes(layers);
ann->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM);
ann->setTrainMethod(cv::ml::ANN_MLP::BACKPROP, 0.1, 0.1);
ann->setTermCriteria(cv::TermCriteria(TermCriteria::MAX_ITER, 1000, 1e-6));
Mat trainClasses_onehot(train_sample_nums, numCharacters, CV_32FC1); //转化为独热编码的形式
for (int i = 0; i < trainClasses_onehot.rows; i++)
for (int j = 0; j < trainClasses_onehot.cols; j++)
{
if (trainClassess.at<int>(i, 0) == j) //那样本的分类应该为0,1,2,3,4之类的
trainClasses_onehot.at<float>(i, j) = 1;
else
trainClasses_onehot.at<float>(i, j) = 0;
}
cv::Ptr<TrainData> TrainData = TrainData::create(trainData, ROW_SAMPLE, trainClasses_onehot);
ann->train(TrainData);
for (int i = 0; i < img.rows; i++)
for (int j = 0; j < img.cols; j++)
{
Mat sample_mat = (Mat_<float>(1, 2) << i, j);
Mat response_mat(1, 2, CV_32FC1);
ann->predict(sample_mat, response_mat);
Point maxLoc;
double maxval;
minMaxLoc(response_mat, 0, &maxval, 0, &maxLoc);
if (maxLoc.x == 0)
img.at<Vec3b>(i, j) = Vec3b(0, 255, 0);
else if (maxLoc.x == 1)
img.at<Vec3b>(i, j) = Vec3b(255, 0, 0);
/*
对应输出层为1的response
float response = response_mat.ptr<float>(0)[0];
if (response > 0.5)
img.at<Vec3b>(i, j) = Vec3b(0, 255, 0);
else if (response < 0.5)
img.at<Vec3b>(i, j) = Vec3b(255, 0, 0);
*/
}
for (int i = 0; i < train_sample_nums / 2; i++)
{
Point pt;
pt.x = round(trainData1.at<float>(i, 0));
pt.y = round(trainData1.at<float>(i, 1));
circle(img, pt, 1, Scalar(0, 255, 255));
pt.x = round(trainData2.at<float>(i, 0));
pt.y = round(trainData2.at<float>(i, 1));
circle(img, pt, 1, Scalar(255, 255, 0));
}
waitKey(0);
}
图像分割待填坑